home *** CD-ROM | disk | FTP | other *** search
- //
- // BEGIN FLOCK GPL
- //
- // Copyright Flock Inc. 2005-2007
- // http://flock.com
- //
- // This file may be used under the terms of of the
- // GNU General Public License Version 2 or later (the "GPL"),
- // http://www.gnu.org/licenses/gpl.html
- //
- // Software distributed under the License is distributed on an "AS IS" basis,
- // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- // for the specific language governing rights and limitations under the
- // License.
- //
- // END FLOCK GPL
- //
-
- const MS_CONTRACTID = '@flock.com/metrics-service;1';
- const MS_CLASSID = Components.ID('{bc1358fa-6e36-4d0d-a401-b0b02111114c}');
- const MS_CLASSNAME = 'Flock Metrics Service';
-
-
- const ENABLED_BY_DEFAULT = true;
-
- const LOGGING_URL = 'http://metrics.flock.com/collect.php';
-
- const PREF_FLOCK_METRICS_ENABLED = 'flock.metrics.enabled';
- const PREF_FLOCK_METRICS_SHUTDOWN = 'flock.metrics.shutdownClean';
-
- const PREF_FLOCK_METRICS_INTERVAL = 'flock.metrics.interval';
- const DEFAULT_METRICS_INTERVAL = 86400;
-
- const PREF_FLOCK_FIRST_RUN_DATE = 'flock.first_run.bigDate';
- const PREF_USERAGENT_EDITION = 'general.useragent.edition';
-
- const PREF_DEFAULT_SEARCH_ENGINE = 'browser.search.selectedEngine';
- const PREF_DEFAULT_ENGINE_NAME = 'browser.search.defaultenginename';
- const PREF_MYWORLD_SEARCH_ENGINE = 'flock.myworld.currentEngine';
- const PREF_START_PAGE = 'browser.startup.homepage';
- const PREF_FEED_SELECTED_ACTION = 'browser.feeds.handler';
-
- const URI_BRAND_PROPERTIES = 'chrome://branding/locale/brand.properties';
-
- const MYWORLD_URL = 'about:myworld';
-
-
- const Cc = Components.classes;
- const Ci = Components.interfaces;
- const Cr = Components.results;
-
- /* from nspr's prio.h */
- const PR_RDONLY = 0x01;
- const PR_WRONLY = 0x02;
- const PR_RDWR = 0x04;
- const PR_CREATE_FILE = 0x08;
- const PR_APPEND = 0x10;
- const PR_TRUNCATE = 0x20;
- const PR_SYNC = 0x40;
- const PR_EXCL = 0x80;
-
-
- var gApp = null;
-
-
- function getObserverService() {
- return Cc['@mozilla.org/observer-service;1']
- .getService(Ci.nsIObserverService);
- }
-
- function getCharPref(prefName, defaultValue) {
- try {
- var prefs = Cc['@mozilla.org/preferences-service;1']
- .getService(Ci.nsIPrefBranch);
- return prefs.getCharPref(prefName);
- }
- catch (e) {
- return defaultValue;
- }
- }
-
- function getIntPref(prefName, defaultValue) {
- try {
- var prefs = Cc['@mozilla.org/preferences-service;1']
- .getService(Ci.nsIPrefBranch);
- return prefs.getIntPref(prefName);
- }
- catch (e) {
- return defaultValue;
- }
- }
-
- function getBoolPref(prefName, defaultValue) {
- try {
- var prefs = Cc['@mozilla.org/preferences-service;1']
- .getService(Ci.nsIPrefBranch);
- return prefs.getBoolPref(prefName);
- }
- catch (e) {
- return defaultValue;
- }
- }
-
-
- function MetricsService() {
- gApp = Cc['@mozilla.org/xre/app-info;1']
- .getService(Ci.nsIXULAppInfo)
- .QueryInterface(Ci.nsIXULRuntime);
-
- this._createStore();
-
- var obs = getObserverService();
-
- obs.addObserver(this, 'flock-data-ready', false);
- obs.addObserver(this, 'quit-application', false);
- obs.addObserver(this, 'xpcom-shutdown', false);
-
- this._enabled = ENABLED_BY_DEFAULT;
-
- if (this._enabled)
- this.observe(null, 'nsPref:changed', null);
- }
-
- MetricsService.prototype = {
- _start: function MS__start() {
- this._logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
- this._logger.init('metrics');
- this._logger.info('starting up...');
-
- this._saveTimer = null;
-
- this._coop = Cc['@flock.com/singleton;1'].getService(Ci.flockISingleton)
- .getSingleton('chrome://browser/content/flock/common/load-faves-coop.js')
- .wrappedJSObject;
-
- var prefs = Cc['@mozilla.org/preferences-service;1']
- .getService(Ci.nsIPrefBranch2);
- prefs.addObserver(PREF_FLOCK_METRICS_ENABLED, this, false);
-
- this.observe(null, 'nsPref:changed', null);
-
- this._watchAuthEvents();
-
- if (prefs.prefHasUserValue(PREF_FLOCK_METRICS_SHUTDOWN) &&
- prefs.getBoolPref(PREF_FLOCK_METRICS_SHUTDOWN) == false)
- this.reportCount('crash');
-
- prefs.setBoolPref(PREF_FLOCK_METRICS_SHUTDOWN, false);
-
- prefs.QueryInterface(Ci.nsIPrefService);
- prefs.savePrefFile(null);
-
- this.reportCount('start');
-
- var tm = Cc['@mozilla.org/updates/timer-manager;1']
- .getService(Ci.nsIUpdateTimerManager);
- var interval = getIntPref(PREF_FLOCK_METRICS_INTERVAL,
- DEFAULT_METRICS_INTERVAL);
- tm.registerTimer('background-metrics-timer', this, interval);
- },
- _stop: function MS__stop() {
- if (this._saveTimer) {
- this._saveTimer.cancel();
- this._saveTimer = null;
- }
-
- this._saveStore();
-
- var prefs = Cc['@mozilla.org/preferences-service;1']
- .getService(Ci.nsIPrefBranch);
- prefs.setBoolPref(PREF_FLOCK_METRICS_SHUTDOWN, true);
- },
- _shutdown: function MS__shutdown() {
- gApp = null;
- },
-
- _configure: function MS__configure() {
- this._enabled = getBoolPref(PREF_FLOCK_METRICS_ENABLED, ENABLED_BY_DEFAULT);
-
- if (!this._enabled) {
- var file = this._getMetricsFile();
- try {
- file.remove(false);
- }
- catch (e) { }
- }
- },
- _prefChanged: function MS__prefChanged(state) {
- this._configure();
- },
-
- observe: function MS_observe(subject, topic, state) {
- var obs = getObserverService();
-
- switch (topic) {
- case 'flock-data-ready':
- obs.removeObserver(this, 'flock-data-ready');
- this._start();
- break;
-
- case 'quit-application':
- obs.removeObserver(this, 'quit-application');
- this._stop();
- break;
-
- case 'xpcom-shutdown':
- obs.removeObserver(this, 'xpcom-shutdown');
- this._shutdown();
- break;
-
- case 'nsPref:changed':
- this._prefChanged(state);
- break;
-
- case 'timer-callback':
- this._saveTimer = null;
- this._saveStore();
- break;
- }
- },
-
- notify: function MS_notify(timer) {
- this._sendReport();
- },
-
- _getMetricsFile: function MS__getMetricsFile() {
- var file = Cc['@mozilla.org/file/directory_service;1']
- .getService(Ci.nsIProperties).get('ProfD', Ci.nsILocalFile);
- file.append('mstore.js');
- return file;
- },
-
- _fillBaseInfo: function MS__fillBaseInfo(store) {
- store['version'] = gApp.version;
- store['buildID'] = gApp.appBuildID;
- store['firstRun'] = getCharPref(PREF_FLOCK_FIRST_RUN_DATE, '0');
- store['edition'] = getCharPref(PREF_USERAGENT_EDITION, '');
-
- var sbs = Cc['@mozilla.org/intl/stringbundle;1']
- .getService(Ci.nsIStringBundleService);
- var bundle = sbs.createBundle(URI_BRAND_PROPERTIES);
-
- store['product'] = bundle.GetStringFromName('brandShortName');
- },
- _fillPrefInfo: function MS__fillPrefInfo(store) {
- var prefs = Cc['@mozilla.org/preferences-service;1']
- .getService(Ci.nsIPrefBranch);
-
- var defaultEngine;
- try {
- defaultEngine = prefs.getCharPref(PREF_DEFAULT_SEARCH_ENGINE);
- }
- catch (e) {
- defaultEngine = prefs.getComplexValue(PREF_DEFAULT_ENGINE_NAME,
- Ci.nsIPrefLocalizedString).data;
- }
-
- if (!defaultEngine)
- defaultEngine = '';
-
- store['default search engine'] = defaultEngine;
-
- var startPageHasMyWorld = false;
- try {
- var startPage = prefs.getComplexValue(PREF_START_PAGE,
- Ci.nsIPrefLocalizedString).data;
- var startPages = startPage.split('|');
- for each (var url in startPages) {
- // Trim leading/trailing whitespace from URL
- url = url.replace(/(^\s+)|(\s+$)/g, "");
- if (url == MYWORLD_URL) {
- startPageHasMyWorld = true;
- break;
- }
- }
- }
- catch (e) { }
-
- store['myworld start page'] = startPageHasMyWorld;
-
- store['myworld search engine'] =
- getCharPref(PREF_MYWORLD_SEARCH_ENGINE, '');
-
- var action = getCharPref(PREF_FEED_SELECTED_ACTION);
- var feedReader;
- if (action == 'ask')
- feedReader = 'news';
- else if (action == 'bookmarks')
- feedReader = 'livemarks';
- else
- feedReader = 'other';
-
- store['default feed reader'] = feedReader;
-
- store['metrics enabled'] = this._enabled;
- },
-
- _initStore: function MS__initStore() {
- this._store = {};
- this._fillBaseInfo(this._store);
- this._fillPrefInfo(this._store);
- },
- _createStore: function MS__createStore() {
- var store;
-
- try {
- var file = this._getMetricsFile();
-
- var stream = Cc['@mozilla.org/network/file-input-stream;1']
- .createInstance(Ci.nsIFileInputStream);
- stream.init(file, PR_RDONLY, 0, 0);
-
- var cvstream = Cc['@mozilla.org/intl/converter-input-stream;1']
- .createInstance(Ci.nsIConverterInputStream);
- cvstream.init(stream, 'UTF-8', 1024,
- Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
-
- var content = '';
- var data = {};
- while (cvstream.readString(4096, data)) {
- content += data.value;
- }
- cvstream.close();
-
- store = content.replace(/\r\n?/g, '\n');
- }
- catch (e) {
- store = null;
- }
-
- if (store) {
- try {
- this._store = this._safeEval(store);
- return;
- }
- catch (e) { }
- }
-
- this._initStore();
- },
-
- _saveStore: function MS__saveStore() {
- if (!this._enabled) return;
-
- try {
- var file = this._getMetricsFile();
-
- var ostream = Cc['@mozilla.org/network/safe-file-output-stream;1']
- .createInstance(Ci.nsIFileOutputStream);
- ostream.init(file, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, 0);
-
- var converter = Cc['@mozilla.org/intl/scriptableunicodeconverter']
- .createInstance(Ci.nsIScriptableUnicodeConverter);
- converter.charset = 'UTF-8';
-
- var data = this._store.toSource();
- var convdata = converter.ConvertFromUnicode(data) + converter.Finish();
-
- ostream.write(convdata, convdata.length);
-
- if (ostream instanceof Ci.nsISafeOutputStream) {
- ostream.finish();
- } else {
- ostream.close();
- }
- }
- catch (e) { }
- },
-
- _scheduleSaveStore: function MS__scheduleSaveStore() {
- if (!this._enabled) return;
-
- if (!this._saveTimer) {
- this._saveTimer = Cc['@mozilla.org/timer;1'].createInstance(Ci.nsITimer);
- this._saveTimer.init(this, 5000, Ci.nsITimer.TYPE_ONE_SHOT);
- }
- },
-
- _watchAuthEvents: function MS__watchAuthEvents() {
- var RDFS = Cc['@mozilla.org/rdf/rdf-service;1']
- .getService(Ci.nsIRDFService);
- var isAuth = RDFS.GetResource("http://flock.com/rdf#isAuthenticated");
-
- var faves = Cc['@mozilla.org/rdf/datasource;1?name=flock-favorites']
- .getService(Ci.flockIRDFObservable);
- faves.addArcObserver(Ci.flockIRDFObserver.WATCH_TYPES, null,
- isAuth, RDFS.GetLiteral("true"), this);
- },
- rdfChanged: function MS_rdfChanged(ds, type, rsrc, pred, obj, oldObj) {
- var acct = this._coop.get_from_resource(rsrc);
- var svc = Cc[acct.serviceId].getService(Ci.flockIWebService);
- this.reportCount('login ' + svc.shortName);
- },
-
- report: function MS_report(key, value) {
- if (!this._enabled) return;
- this._store[key] = value;
- this._scheduleSaveStore();
- },
- reportNow: function MS_reportNow(key, value) {
- if (this._enabled)
- this._sendQuickReport(key, value);
- },
- reportCount: function MS_reportCount(key) {
- if (!this._enabled) return;
-
- if (this._store[key])
- this._store[key]++;
- else
- this._store[key] = 1;
-
- this._scheduleSaveStore();
- },
-
- _sendReport: function MS__sendReport() {
- if (this._enabled) {
- this._fillBaseInfo(this._store);
- this._fillPrefInfo(this._store);
- this._sendData(this._toJSONString(this._store));
- } else {
- this._sendQuickReport('metrics enabled', false);
- }
-
- this._initStore();
- this._saveStore();
- },
- _sendQuickReport: function MS__sendQuickReport(key, value) {
- var data = {};
- data[key] = value;
- this._fillBaseInfo(data);
-
- this._sendData(this._toJSONString(data));
- },
-
- _sendData: function MS__sendData(data) {
- if (gApp.appBuildID == '0000000000')
- return;
-
- try {
- var hr = Cc['@mozilla.org/xmlextras/xmlhttprequest;1']
- .createInstance(Ci.nsIXMLHttpRequest);
-
- var self = this;
- hr.onerror = function(evt) { self._onError(evt); };
- hr.onload = function(evt) { self._onLoad(evt); };
-
- hr.detachLoadGroup = true;
- hr.open('POST', LOGGING_URL);
- hr.send(data);
- }
- catch (e) {
- this._onError(null);
- }
- },
- _onError: function MS__onError(evt) {
- },
- _onLoad: function MS__onLoad(evt) {
- },
-
- /* The next two functions taken from nsSessionStore.js */
-
- /**
- * safe eval'ing
- */
- _safeEval: function sss_safeEval(aStr) {
- var s = new Components.utils.Sandbox("about:blank");
- return Components.utils.evalInSandbox(aStr, s);
- },
-
- /**
- * Converts a JavaScript object into a JSON string
- * (see http://www.json.org/ for the full grammar).
- *
- * The inverse operation consists of eval("(" + JSON_string + ")");
- * and should be provably safe.
- *
- * @param aJSObject is the object to be converted
- * @return the object's JSON representation
- */
- _toJSONString: function sss_toJSONString(aJSObject) {
- // these characters have a special escape notation
- const charMap = { "\b": "\\b", "\t": "\\t", "\n": "\\n", "\f": "\\f",
- "\r": "\\r", '"': '\\"', "\\": "\\\\" };
- // we use a single string builder for efficiency reasons
- var parts = [];
-
- // this recursive function walks through all objects and appends their
- // JSON representation to the string builder
- function jsonIfy(aObj) {
- if (typeof aObj == "boolean") {
- parts.push(aObj ? "true" : "false");
- }
- else if (typeof aObj == "number" && isFinite(aObj)) {
- // there is no representation for infinite numbers or for NaN!
- parts.push(aObj.toString());
- }
- else if (typeof aObj == "string") {
- aObj = aObj.replace(/[\\"\x00-\x1F\u0080-\uFFFF]/g, function($0) {
- // use the special escape notation if one exists, otherwise
- // produce a general unicode escape sequence
- return charMap[$0] ||
- "\\u" + ("0000" + $0.charCodeAt(0).toString(16)).slice(-4);
- });
- parts.push('"' + aObj + '"')
- }
- else if (aObj == null) {
- parts.push("null");
- }
- else if (aObj instanceof Array) {
- parts.push("[");
- for (var i = 0; i < aObj.length; i++) {
- jsonIfy(aObj[i]);
- parts.push(",");
- }
- if (parts[parts.length - 1] == ",")
- parts.pop(); // drop the trailing colon
- parts.push("]");
- }
- else if (typeof aObj == "object") {
- parts.push("{");
- for (var key in aObj) {
- jsonIfy(key.toString());
- parts.push(":");
- jsonIfy(aObj[key]);
- parts.push(",");
- }
- if (parts[parts.length - 1] == ",")
- parts.pop(); // drop the trailing colon
- parts.push("}");
- }
- else {
- throw new Error("No JSON representation for this object!");
- }
- }
- jsonIfy(aJSObject);
-
- var newJSONString = parts.join(" ");
- // sanity check - so that API consumers can just eval this string
- if (/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
- newJSONString.replace(/"(\\.|[^"\\])*"/g, "")
- ))
- throw new Error("JSON conversion failed unexpectedly!");
-
- return newJSONString;
- },
-
- getInterfaces: function MS_getInterfaces(countRef) {
- var interfaces = [Ci.flockIMetricsService, Ci.flockIRDFObserver,
- Ci.nsITimerCallback, Ci.nsIObserver, Ci.nsIClassInfo,
- Ci.nsISupports];
- countRef.value = interfaces.length;
- return interfaces;
- },
- getHelperForLanguage: function MS_getHelperForLanguage(language) {
- return null;
- },
- contractID: MS_CONTRACTID,
- classDescription: MS_CLASSNAME,
- classID: MS_CLASSID,
- implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
- flags: Ci.nsIClassInfo.SINGLETON,
-
- QueryInterface: function MS_QueryInterface(iid) {
- if (iid.equals(Ci.flockIMetricsService) ||
- iid.equals(Ci.flockIRDFObserver) ||
- iid.equals(Ci.nsITimerCallback) ||
- iid.equals(Ci.nsIObserver) ||
- iid.equals(Ci.nsIClassInfo) ||
- iid.equals(Ci.nsISupports))
- return this;
- throw Cr.NS_ERROR_NO_INTERFACE;
- }
- }
-
-
- function GenericComponentFactory(ctor) {
- this._ctor = ctor;
- }
-
- GenericComponentFactory.prototype = {
-
- _ctor: null,
-
- // nsIFactory
- createInstance: function(outer, iid) {
- if (outer != null)
- throw Cr.NS_ERROR_NO_AGGREGATION;
- return (new this._ctor()).QueryInterface(iid);
- },
-
- // nsISupports
- QueryInterface: function(iid) {
- if (iid.equals(Ci.nsIFactory) ||
- iid.equals(Ci.nsISupports))
- return this;
- throw Cr.NS_ERROR_NO_INTERFACE;
- },
- };
-
- var Module = {
- QueryInterface: function(iid) {
- if (iid.equals(Ci.nsIModule) ||
- iid.equals(Ci.nsISupports))
- return this;
-
- throw Cr.NS_ERROR_NO_INTERFACE;
- },
-
- getClassObject: function(cm, cid, iid) {
- if (!iid.equals(Ci.nsIFactory))
- throw Cr.NS_ERROR_NOT_IMPLEMENTED;
-
- if (cid.equals(MS_CLASSID))
- return new GenericComponentFactory(MetricsService)
-
- throw Cr.NS_ERROR_NO_INTERFACE;
- },
-
- registerSelf: function(cm, file, location, type) {
- var cr = cm.QueryInterface(Ci.nsIComponentRegistrar);
- cr.registerFactoryLocation(MS_CLASSID, MS_CLASSNAME, MS_CONTRACTID,
- file, location, type);
-
- var catman = Cc['@mozilla.org/categorymanager;1']
- .getService(Ci.nsICategoryManager);
- catman.addCategoryEntry('flock-startup', MS_CLASSNAME,
- 'service,' + MS_CONTRACTID,
- true, true);
- },
-
- unregisterSelf: function(cm, location, type) {
- var cr = cm.QueryInterface(Ci.nsIComponentRegistrar);
- cr.unregisterFactoryLocation(MS_CLASSID, location);
- },
-
- canUnload: function(cm) {
- return true;
- },
- };
-
- function NSGetModule(compMgr, fileSpec)
- {
- return Module;
- }
-